package net.m2technologies.open_arm.utilities.generic_object_functions;

import java.lang.reflect.Field;

/**
 * Copyright 2005 Mark Masterson<br> <br> Licensed under the Apache License, Version 2.0 (the "License");<br> you may
 * not use this file except in compliance with the License.<br> You may obtain a copy of the License at<br> <br>
 * http://www.apache.org/licenses/LICENSE-2.0<br> <br> Unless required by applicable law or agreed to in writing,
 * software<br> distributed under the License is distributed on an "AS IS" BASIS,<br> WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, either express or implied.<br> See the License for the specific language governing permissions and<br>
 * limitations under the License.<br>
 * <p/>
 * <p>Description: </p>
 *
 * @author Mark Masterson
 * @version 0.010
 */
class EqualsWorker extends ReflectiveWorker {

    private boolean equalsResursiveResult = true;

    public boolean equals(final Object objectA, final Object objectB) {
        if (objectA == objectB) return true;
        if (!this.equalsResursiveResult) {
            this.equalsResursiveResult = true;
        }
        final Class clazzA = objectA.getClass();
        final Class clazzB = objectB.getClass();
        if (!clazzA.equals(clazzB)) return false;
        try {
            //peel out and apart any arrays, including arrays of primitives...
            final Object[] tempArr = isObjectArray(objectA);
            if (null != tempArr) {
                return handleObjectArrays(objectB, tempArr);
            } else {//not an array... Might contain arrays, however...
                final Field[] fields = getAccessibleFields(clazzA);
                for (int i = 0; i < fields.length; i++) {
                    final Field field = fields[i];
                    if (!getFieldValueAsString(field, objectA).equals(getFieldValueAsString(field, objectB))) {
                        return false;
                    }
                }
            }
        } catch (SecurityException ex) {
            throw new RuntimeException(ex);
        }
        handleRecursive(clazzA, clazzB, objectA, objectB);
        return this.equalsResursiveResult;
    }

    private boolean handleObjectArrays(final Object objectB, final Object[] tempArr) {
        final Object[] secondTempArr = isObjectArray(objectB);
        if (null == secondTempArr) return false;
        if (tempArr.length != secondTempArr.length) return false;
        return handleArraysForEquals(tempArr, secondTempArr);
    }

    private void handleRecursive(final Class clazzA, final Class clazzB, final Object objectA, final Object objectB) {
        final Class superClazzA = clazzA.getSuperclass();
        final Class superClazzB = clazzB.getSuperclass();
        if (shouldBeRecursive(superClazzA, clazzA, superClazzB, clazzB)) {
            equalsRecursive(objectA, superClazzA, objectB, superClazzB);
        }
    }

    private boolean shouldBeRecursive(final Class superClazzA,
                                      final Class clazzA,
                                      final Class superClazzB,
                                      final Class clazzB) {
        return !(superClazzA == clazzA
                 || superClazzB == clazzB
                 || !(!superClazzA.equals(Object.class)
                      && !superClazzB.equals(Object.class)));
    }

    private boolean equalsRecursive(final Object objectA,
                                    final Class clazzA,
                                    final Object objectB,
                                    final Class clazzB) {
        if (!clazzA.equals(clazzB)) {
            this.equalsResursiveResult = false;
            return this.equalsResursiveResult;
        }
        try {
            final Field[] fields = getAccessibleFields(clazzA);
            for (int i = 0; i < fields.length; i++) {
                final Field field = fields[i];
                if (!getFieldValueAsString(field,
                                           objectA).equals(getFieldValueAsString(field, objectB))) {
                    this.equalsResursiveResult = false;
                }
            }
        } catch (SecurityException ex) {
            throw new RuntimeException(ex);
        } catch (StackOverflowError er) {
            return this.equalsResursiveResult;
        }
        final Class superClazzA = clazzA.getSuperclass();
        final Class superClazzB = clazzB.getSuperclass();
        if (superClazzA != clazzA && !superClazzA.equals(Object.class)) {
            equalsRecursive(objectA, superClazzA, objectB, superClazzB);
        }
        return this.equalsResursiveResult;
    }

    private boolean handleArraysForEquals(final Object[] tempArr, final Object[] secondTempArr) {
        for (int i = 0; i < tempArr.length; i++) {
            if (!getObjectValueAsString(tempArr[i])
                    .equals(getObjectValueAsString(secondTempArr[i]))) {
                return false;
            }
        }
        return true;
    }
}
